02 通过闭包对象管理程序中状态的变化
值的(不)可变
值一般被分为两种:原始类型和对象类型
原始类型:本身是不可变的
2 = 2.5 // invalid
对象类型:一种数据结构或容器
React.js 中的 props 和 state
- props 通常是作为一个外部参数传入到函数里,作为静态元素输出在 UI 中渲染;
- state 是一个内部变量,作为动态元素输出在 UI 中渲染,并根据行为更新状态。
结构型值的不可变
应用需要和用户交互,有交互就需要管理值的状态(state) 和围绕值设计一系列行为(behavior)。这个过程中需要考虑的就是一个值的结构性不可变的问题。
闭包和对象
闭包(closure)和对象(object)都可以对一个状态值进行封装和创建行为。
闭包最大的特点是可以突破生命周期和作用域的限制,即时间和空间的控制。
突破生命周期的限制:当一个外部函数内嵌一个内部函数时,如果内嵌函数引用了外部函数的变量,这个变量就会突破生命周期的限制,在函数结束执行后仍然存在。
突破作用域的限制:可以把一个内部函数返回成一个方法在外部调用。
function counter() {
let curVal = 0;
function counting() {
curVal++;
}
function getCount() {
console.log(curVal);
}
return { counting, getCount };
}
var counter1 = counter();
counter1.counting();
counter1.counting();
counter1.counting();
counter1.getCount(); // 3
var counter = {
curVal: 0,
counting() {
this.curVal++;
console.log(this.curVal);
},
};
counter.counting(); // 1
counter.counting(); // 2
counter.counting(); // 3
两者的差异
属性的查改
闭包:内部的值是对外不可见的,只能通过外部函数中返回的内部函数修改。可以细粒度地控制想要暴露或隐藏的属性以及相关的操作。
对象:可以直接获取对象中的属性和重新赋值。可以通过 Object.freeze() 达到遵循不可变的原则。
状态的拷贝
通过拷贝管理状态
通过拷贝 + 更新的方式,不对原始的对象和数组值做改变,而是在拷贝的版本上做变更。
// 数组浅拷贝
var a = [1, 2];
var b = [...a];
b.push(3);
a; // [1,2]
b; // [1,2,3]
// 对象浅拷贝
var o = {
x: 1,
y: 2,
};
var p = { ...o };
p.y = 3;
o.y; // 2
p.y; // 3
解决拷贝性能问题
值不停在改变,如果每次都拷贝,会占据大量内存。可以用一个类似链表的结构解决。
性能
对象的内存和运算通常要优于闭包。闭包中每次使用都会创建一个新的函数表达。对象中通过 bind 将 this 绑定到要引用的对象上即可。